#include "filebuffer.h"
#include <iostream>

#include <QDebug>
#include <QFile>
#include <QIODevice>

const qint64 BLOCK_SIZE = 32 * 1024;

FileBuffer::FileBuffer():
    state(FREE),
    data(NULL),
    bytesProcessed(-1),    
    errorStream(&errorString)
{    
//    qDebug() << "FileBuffer::FileBuffer()";
}

FileBuffer::~FileBuffer(){
//    qDebug() << "FileBuffer::~FileBuffer()";
    delete data;
}

QString FileBuffer::holdLoad()
{
    return QString();
}

void FileBuffer::loadFile(QString fName)
{
//    qDebug() << QString("FileBuffer::loadFile(%1)").arg(fName);
    errorReset();    
    QFile file(fName);
    if(!file.open(QIODevice::ReadOnly) || (file.error() != QFile::NoError)){
        errorBegin() << QObject::tr("Не могу открыть файл на чтение ") << fName << endl \
                     << file.errorString() << errorEnd();
//        clearData();
        if(data != NULL){
            setState(LOADED);
        } else {
            setState(FREE);
        }
        return;
    }    
    setFileSize(file.size());
    setFileName(fName);
    setState(LOADING);
    delete data;
    data = new QLinkedList<QByteArray>();
    setBytesProcessed(0);
    char* buf = new char[BLOCK_SIZE];
    qint64 bytesRead;
    bool canWork = true;
    enableWork();
    while (((bytesRead = file.read(buf, BLOCK_SIZE)) > 0) && (canWork = this->canWork())) {
        QByteArray block(buf, bytesRead);
        data->append(block);
        incBytesProcessed(block.size());
    }
    delete buf;
    file.close();
    if(!canWork){
        clearData();
        errorBegin() << tr("Загрузка файла отменена пользователем") << errorEnd();
        return;
    }
    if((bytesRead == -1) || (file.error() != QFile::NoError)){
        clearData();
        errorBegin() << tr("Ошибка при чтении файла ") << fName << endl \
                     << file.errorString() << errorEnd();        
        return;
    }    
    if(fileSize != bytesProcessed){
        clearData();
        errorBegin() << tr("Ошибка при чтении файла ") << fName << endl \
                     << tr("Размер файла на диске (%1) и кол-во считанных байт (%2) не совпадают") \
                        .arg(file.size()).arg(bytesProcessed) << errorEnd();
        return;
    }
    setState(LOADED);    
    return;
}

QString FileBuffer::holdSave()
{
    if(state == IFileBuffer::LOADED){
        return QString();
    } else {
        return tr("Нечего сохранять");
    }
}

void FileBuffer::saveFile(QString fName){
//    qDebug() << QString("FileBuffer::saveFile(%1)").arg(fName);
    errorReset();
    QFile file(fName);
    if(!file.open(QIODevice::WriteOnly) || (file.error() != QFile::NoError)){
        errorBegin() << tr("Не могу открыть файл на запись ") << fName << endl \
                     << file.errorString() << errorEnd();
        setState(LOADED);
        return;
    }
    setState(SAVING);
    setBytesProcessed(0);
    bool canWork = true;
    enableWork();
    for(QLinkedList<QByteArray>::iterator it = data->begin(); (it != data->end()) && (canWork = this->canWork()); it++){
        qint64 bytesWrite = file.write(*it);
        if((bytesWrite == it->size()) && (file.error() == QFile::NoError)){
            incBytesProcessed(bytesWrite);
        } else {
            errorBegin() << tr("Ошибка при записи файла ") << fName << endl \
                         << file.errorString() << errorEnd();
            file.close();
            setState(LOADED);
            return;
        }
    }
    file.flush();
    file.close();
    if(!canWork) {
        errorBegin() << tr("Сохранение файла отменено пользователем") << errorEnd();
        setState(LOADED);
        return;
    }
    if(file.size() != bytesProcessed){
        errorBegin() << tr("Ошибка при записи файла ") << fName << endl\
                     << tr("Размер файла на диске после записи (%1) и размер образа файла (%2) не совпадают")
                       .arg(file.size()).arg(bytesProcessed) << errorEnd();
        setState(LOADED);
        return;
    }
    setState(LOADED);    
    return;
}

void FileBuffer::release()
{

}

QString FileBuffer::holdClear()
{
    return QString();
}

void FileBuffer::clearData()
{
    delete data;
    data = NULL;
    setBytesProcessed(-1);
    setFileName(QString());
    setState(FREE);
}

QString FileBuffer::getLastError()
{
    return errorString;
}

IFileBuffer::State FileBuffer::getState()
{
    return state;
}

QString FileBuffer::getFileName()
{
    return fName;
}

qint64 FileBuffer::getFileSize()
{
    return fileSize;
}

qint64 FileBuffer::getBytesProcessed()
{
    return this->bytesProcessed;
}

void FileBuffer::trigger()
{

}

void FileBuffer::stop()
{

}

bool FileBuffer::canWork()
{
    return true;
}

void FileBuffer::enableWork()
{

}

void FileBuffer::setState(State newState)
{
    state = newState;
}

void FileBuffer::setBytesProcessed(qint64 newSize)
{
    this->bytesProcessed = newSize;
}

void FileBuffer::incBytesProcessed(qint64 incSize)
{
    this->bytesProcessed += incSize;
}

void FileBuffer::setFileSize(qint64 newSize)
{
    this->fileSize = newSize;
}

void FileBuffer::setFileName(QString newFileName)
{
    this->fName = newFileName;
}

void FileBuffer::errorReset()
{    
    errorString = QString();
}

QTextStream& FileBuffer::errorBegin()
{
    return errorStream;
}

QString FileBuffer::errorEnd()
{
    return QString();
}
